package cn.itcast.lucene;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;


public class IndexQueryTest  {

    @Test
    public void testQuery() throws IOException, ParseException {

//    查询解析器，两个参数：查村字段和分词器，（查询分词器必须和创建索引分词器一致）
        QueryParser queryParser=new QueryParser("title",new IKAnalyzer());

        Query query = queryParser.parse("谷歌地图");

        commonSearch(query);
    }

    /**
     * 词条查询，查询条件就是词条，不用分词
     * @throws IOException
     * @throws ParseException
     */
    @Test
    public void testTermQuery() throws IOException, ParseException {

        Query query = new TermQuery(new Term("title","项目"));

        commonSearch(query);
    }

    /**
     * 通配符查询，    星号：* 可以任意多个任意字符
     *                问号：? 可以代表任意一个字符    英文输入法下
     * 通配符查询基本逻辑还是词条查询
     * @throws IOException
     * @throws ParseException
     */
    @Test
    public void testWildcardQuery() throws IOException, ParseException {
        Query query=new WildcardQuery(new Term("title","创始?"));

        commonSearch(query);
    }

    /**
     * 模糊查询，
     * 默认允许对当前关键字操作两次，可以修改：比如修改为0次，这是就是词条查询
     * 常见数量为1次，只允许修改一次
     * @throws IOException
     * @throws ParseException
     */
    @Test
    public void testfuzzyQuery() throws IOException, ParseException {
        Query query=new FuzzyQuery(new Term("title","facabook"),1);

        commonSearch(query);
    }

    /**
     * 数值范围查询
     * 如果某个字段是数据类型，查询时，必须使用数值范围查询！！！
     * @throws IOException
     * @throws ParseException
     */
    @Test
    public void testNumericRangeQuery() throws IOException, ParseException {
//        5个参数：字段；最小；最大；是否包含最小；是否包含最大；
        Query query=NumericRangeQuery.newLongRange("id",3L,5L,true,true);

        commonSearch(query);
    }

    /**
     * 组合查询
     * @throws IOException
     * @throws ParseException
     * MUST VS MUST  && 交集
     * SHOULD VS SHOULD  \\ 并集
     * MUST VS MUST_NOT  ! 非，取反
     */
    @Test
    public void testBooleanQuery() throws IOException, ParseException {
//        5个参数：字段；最小；最大；是否包含最小；是否包含最大；
        Query query1=NumericRangeQuery.newLongRange("id",3L,5L,true,true);
        Query query2=NumericRangeQuery.newLongRange("id",2L,4L,true,true);

        BooleanQuery query = new BooleanQuery();

        query.add(query1, BooleanClause.Occur.SHOULD);
        query.add(query2, BooleanClause.Occur.SHOULD);
        commonSearch(query);
    }

    private void commonSearch(Query query) throws IOException, ParseException {
//      声明索引所在目录，进行搜索
        Directory directory = FSDirectory.open(new File("indexDir"));

//    创建一个读取对象来读取索引目录
        IndexReader indexReader= DirectoryReader.open(directory);

//    搜索对象
        IndexSearcher indexSearcher=new IndexSearcher(indexReader);



//    传入搜索条件以及需要得条目数，（前n个）有多少个显示多少，输出内容是需要的和命中的取小。抽取出来的n，最大值
        TopDocs topDocs=indexSearcher.search(query,Integer.MAX_VALUE);

//    命中得元素的个数
        int totalHits = topDocs.totalHits;
        System.out.println("命中的文档个数:"+totalHits);

//    得分文档数组
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
//    当前文档得分
            float score = scoreDoc.score;

//    文档编号
            int docId = scoreDoc.doc;

            Document document = indexSearcher.doc(docId);

//    根据文档id获取具体的文档
            String id = document.get("id");
            String title = document.get("title");

            System.out.println("得分:"+score+" "+"id:"+id+" "+"title:"+title);
        }
    }

}
